package com.cloudlead.common.lang.persistence;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class Page<T> implements Iterable<T>, Serializable {

    private static final long serialVersionUID = 867755909294344406L;

    private List<T> content = new ArrayList<T>();
    private long totalElements;
    private int size;
    private int page;
    private Sort sort;
    private Filter filter;

    public Page() {
    }

    /**
     * Constructor of {@code Page}.
     *
     * @param content  the content of this page, must not be {@literal null}.
     * @param pageable the paging information, can be {@literal null}.
     * @param total    the total amount of items available
     */
    public Page(List<T> content, PageRequest pageable, long total) {

        if (null == content) {
            throw new IllegalArgumentException("Content must not be null!");
        }

        this.content.addAll(content);
        this.totalElements = total;
        if (null != pageable) {
            this.size = pageable.getSize();
            this.page = pageable.getPage();
            this.sort = pageable.getSort();
            this.filter = pageable.getFilter();
        }
    }

    /**
     * Creates a new {@link Page} with the given content. This will result
     * in the created {@link Page} being identical to the entire {@link List}.
     *
     * @param content must not be {@literal null}.
     */
    public Page(List<T> content) {
        this(content, null, null == content ? 0 : content.size());
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public Sort getSort() {
        return sort;
    }

    public void setSort(Sort sort) {
        this.sort = sort;
    }

    public Filter getFilter() {
        return filter;
    }

    public void setFilter(Filter filter) {
        this.filter = filter;
    }

    public int getTotalPages() {
        int ceil = (int) Math.ceil((double) totalElements / (double) getSize());
        return getSize() == 0 ? 0 : ceil;
    }

    public int getNumberOfElements() {
        return content.size();
    }

    public void setTotalElements(long totalElements) {
        this.totalElements = totalElements;
    }

    public long getTotalElements() {
        return totalElements;
    }

    public boolean hasPreviousPage() {
        return getPage() > 1;
    }

    public boolean isFirstPage() {
        return !hasPreviousPage();
    }

    public boolean hasNextPage() {
        return getPage() * getSize() < totalElements;
    }

    public boolean isLastPage() {
        return !hasNextPage();
    }

    public Iterator<T> iterator() {
        return content.iterator();
    }

    public void setContent(List<T> content) {
        this.content = content;
    }

    public List<T> getContent() {
        return Collections.unmodifiableList(content);
    }

    public boolean hasContent() {
        return !content.isEmpty();
    }


    @Override
    public String toString() {

        String contentType = "UNKNOWN";

        if (content.size() > 0) {
            contentType = content.get(0).getClass().getName();
        }

        return String.format("Page %s of %d containing %d %s instances",
                getPage(), getTotalPages(), getNumberOfElements(), contentType);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Page<?> page = (Page<?>) o;

        if (totalElements != page.totalElements) return false;
        if (size != page.size) return false;
        if (this.page != page.page) return false;
        if (content != null ? !content.equals(page.content) : page.content != null) return false;
        if (sort != null ? !sort.equals(page.sort) : page.sort != null) return false;
        return !(filter != null ? !filter.equals(page.filter) : page.filter != null);

    }

    @Override
    public int hashCode() {
        int result = content != null ? content.hashCode() : 0;
        result = 31 * result + (int) (totalElements ^ (totalElements >>> 32));
        result = 31 * result + size;
        result = 31 * result + page;
        result = 31 * result + (sort != null ? sort.hashCode() : 0);
        result = 31 * result + (filter != null ? filter.hashCode() : 0);
        return result;
    }
}
